package com.oj.util;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gargoylesoftware.htmlunit.BrowserVersion;
import com.gargoylesoftware.htmlunit.NicelyResynchronizingAjaxController;
import com.gargoylesoftware.htmlunit.SilentCssErrorHandler;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.oj.entity.Attend;
import com.oj.entity.RecentContest;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.boot.system.ApplicationHome;

import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
public class OJCollector {
    public static List<RecentContest> CollectHduContests() {
        try {
            List<RecentContest> contestList = new ArrayList<>();
            Document document = Jsoup.connect("http://acm.hdu.edu.cn/contests/contest_list.php").get();
            Elements table = document.select("table.table_text");
            Elements trs = table.select("tr");
            for (int i = 1; i < trs.size(); ++i) {
                Element tr = trs.get(i);
                Elements tds = tr.select("td");
                if (!tds.get(4).text().equals("Pending") && !tds.get(4).text().equals("Running") ) {
                    continue;
                }
                Date d = DateUtil.StringToDate(tds.get(2).text(), "yyyy-MM-dd HH:mm:ss");
                RecentContest contest = new RecentContest();
                contest.setName(tds.get(1).text());
                contest.setLink("http://acm.hdu.edu.cn" + tds.get(1).select("a[href]").attr("href"));
                contest.setOj("HDU");
                contest.setAccess(tds.get(3).text());
                contest.setStart_date(d);
                contest.setStart_time(DateUtil.DateToString(d,"yyyy-MM-dd HH:mm:ss"));
                contest.setWeek(DateUtil.getWeek(d));
                contestList.add(contest);
            }
            return contestList;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static List<RecentContest> CollectAtCoderContests() {
        try {
            List<RecentContest> contestList = new ArrayList<>();
            Document document = Jsoup.connect("https://atcoder.jp/contests/").get();
            Elements div = document.select("#contest-table-upcoming");
            Elements table = div.select("table.table");
            Elements trs = table.select("tr");
            for (int i = 1; i < trs.size(); ++i) {
                Element tr = trs.get(i);
                Elements tds = tr.select("td");
                RecentContest contest = new RecentContest();
                Date d = DateUtil.StringToDate(tds.get(0).text(), "yyyy-MM-dd HH:mm:ss");
                Calendar calendar = Calendar.getInstance();
                calendar.setTime(d);
                calendar.add(calendar.HOUR, -1);
                SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                d = calendar.getTime();
                contest.setStart_time(fmt.format(d));
                contest.setName(tds.get(1).text());
                contest.setLink("https://atcoder.jp" + tds.get(1).select("a[href]").attr("href"));
                contest.setOj("AtCoder");
                contest.setAccess("");
                contest.setWeek(DateUtil.getWeek(d));
                contest.setStart_date(d);
                contestList.add(contest);
                //log.info(contest.toString());
            }
            return contestList;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static List<RecentContest> CollectCodeForcesContests() {
        try {
            List<RecentContest> contestList = new ArrayList<>();

            Connection conn = Jsoup.connect("https://codeforces.com/contests").timeout(10000);
            conn.header("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
            conn.header("Accept-Encoding", "gzip, deflate, sdch");
            conn.header("Accept-Language", "zh-CN,zh;q=0.8");
            conn.header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36");

            Document document = conn.get();
            Elements div = document.select("div.contestList");
            Elements divContests = div.select("div.datatable");
            Elements table = divContests.select("table");
            Elements trs = table.get(0).select("tr");
            for (int i = 1; i < trs.size(); ++i) {
                Element tr = trs.get(i);
                Elements tds = tr.select("td");
                RecentContest contest = new RecentContest();
                contest.setName(tds.get(0).text());
                SimpleDateFormat format = new SimpleDateFormat("MMM/dd/yyyy HH:mm", Locale.UK);
                Date date = format.parse(tds.get(2).text());

                Calendar calendar = Calendar.getInstance();
                calendar.setTime(date);
                calendar.add(calendar.HOUR, 5);
                date = calendar.getTime();

                contest.setStart_time(DateUtil.DateToString(date,"yyyy-MM-dd HH:mm:ss"));
                contest.setLink("https://codeforces.com/contests");
                contest.setOj("Codeforces");
                contest.setAccess("");
                contest.setWeek(DateUtil.getWeek(date));
                contest.setStart_date(date);
                contestList.add(contest);
                //log.info(contest.toString());
            }
            return contestList;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }

    public static List<RecentContest> CollectHappyOJContests() {
        try {
            List<RecentContest> contestList = new ArrayList<>();

            Connection conn = Jsoup.connect("https://happyoj.com/contests").timeout(10000);
            conn.header("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
            conn.header("Accept-Encoding", "gzip, deflate, sdch");
            conn.header("Accept-Language", "zh-CN,zh;q=0.8");
            conn.header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36");

            Document document = conn.get();
            Elements table = document.select("table.contest_local_upcoming");
            Elements trs = table.get(0).select("tr");
            for (int i = 1; i < trs.size(); ++i) {
                Element tr = trs.get(i);
                Elements tds = tr.select("td");
                RecentContest contest = new RecentContest();
                contest.setName(tds.get(0).text());
                Date d = DateUtil.StringToDate(tds.get(1).text(), "yyyy-MM-dd HH:mm:ss");
                contest.setStart_time(DateUtil.DateToString(d,"yyyy-MM-dd HH:mm:ss"));
                contest.setLink("https://happyoj.com/contests");
                contest.setOj("HappyOJ");
                contest.setAccess("");
                contest.setWeek(DateUtil.getWeek(d));
                contest.setStart_date(d);
                contestList.add(contest);
                //log.info(contest.toString());
            }
            return contestList;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }
    public static String getPageWaitJS (String url) throws IOException {
        BrowserVersion[] versions = {BrowserVersion.INTERNET_EXPLORER, BrowserVersion.CHROME, BrowserVersion.EDGE};
        WebClient webClient = new WebClient(versions[(int) (versions.length * Math.random())]);
        webClient.setAjaxController(new NicelyResynchronizingAjaxController());
        webClient.setCssErrorHandler(new SilentCssErrorHandler());
        webClient.getCookieManager().setCookiesEnabled(true);
        webClient.getOptions().setUseInsecureSSL(true);
        webClient.getOptions().setJavaScriptEnabled(true); //启用JS解释器，默认为true
        webClient.getOptions().setCssEnabled(false); //禁用css支持
        webClient.getOptions().setRedirectEnabled(true);
        webClient.getOptions().setThrowExceptionOnScriptError(false); //js运行错误时，是否抛出异常
        webClient.waitForBackgroundJavaScript(10000);
        webClient.waitForBackgroundJavaScriptStartingBefore(10000);//设置等待js响应时间
        webClient.getOptions().setAppletEnabled(true);//启用小程序
        webClient.getOptions().setGeolocationEnabled(true);//启用定位
        HtmlPage page = webClient.getPage(url);
        String pageXml = page.asXml(); //以xml的形式获取响应文本
        return pageXml;
    }
    public static List<RecentContest> CollectLeetCodeContests() {
        try {
            log.info("Collect leetcode contest.");
            List<RecentContest> contestList = new ArrayList<>();
            String html = getPageWaitJS("https://leetcode.cn/contest/");
            Document document = Jsoup.parse(html);
            Elements contestsDiv = document.select("div.contest-cards-base");
            Elements contests = contestsDiv.select("div.contest-card-base");

            for (int i = 0; i < contests.size(); ++i) {
                Element c = contests.get(i);
                RecentContest contest = new RecentContest();
                contest.setOj("LeetCode");
                contest.setLink("https://leetcode.cn" + c.select("a[href]").attr("href"));

                Pattern p = Pattern.compile("(\\d{4})-(\\d{1,2})-(\\d{1,2})/(\\d{1,2}):(\\d{1,2})");
                String time = new String();
                if (c.select("div.contest-upcoming").size() > 0) {
                    contest.setName(c.select("div.card-title").text());
                    time = c.select("div.time").text()
                            .replaceAll(" +","")
                            .replaceAll("：", "/");
                } else if (c.select("div.biweekly-ongoing").size() > 0) {
                    contest.setName(c.select("div.title").text());
                    time = c.select("div.contest-time").text()
                            .replaceAll(" +","")
                            .replaceAll("：", "/");

                } else {
                    continue;
                }
                Matcher m = p.matcher(time);
                if(!m.find()) {
                    continue;
                }
                Date d = DateUtil.StringToDate(m.group(0), "yyyy-MM-dd/HH:mm");
                contest.setStart_time(DateUtil.DateToString(d,"yyyy-MM-dd HH:mm:ss"));
                contest.setWeek(DateUtil.getWeek(d));
                contest.setStart_date(d);
                contest.setAccess("");
                contestList.add(contest);
            }
            return contestList;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }
    public static List<RecentContest> CollectLuoGuContests() {
        try {
            log.info("Collect luogu contest.");
            List<RecentContest> contestList = new ArrayList<>();
            String html = getPageWaitJS("https://www.luogu.com.cn/contest/list");
            Document document = Jsoup.parse(html);
            log.info(document.toString());
            return contestList;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }
    public static void CollectContests() {
        Map map = new HashMap();
        map.put("createDate", new Date());

        List<RecentContest> contests = new ArrayList<>();
        List<RecentContest> contestsHdu = CollectHduContests();
        List<RecentContest> contestAtCoder = CollectAtCoderContests();
        List<RecentContest> contestCodeforces = CollectCodeForcesContests();
        List<RecentContest> contestHappyOJ = CollectHappyOJContests();
        List<RecentContest> contestLeetCode = CollectLeetCodeContests();
        if (contestsHdu != null) {
            contests.addAll(contestsHdu);
        }
        if (contestAtCoder != null) {
            contests.addAll(contestAtCoder);
        }
        if (contestCodeforces != null) {
            contests.addAll(contestCodeforces);
        }
        if (contestHappyOJ != null) {
            contests.addAll(contestHappyOJ);
        }
        if (contestLeetCode != null) {
            contests.addAll(contestLeetCode);
        }

        Collections.sort(contests.subList(0, contests.size()), new Comparator<RecentContest>() {
            public int compare(RecentContest r1, RecentContest r2) {
                long diff = r1.getStart_date().getTime() - r2.getStart_date().getTime();
                if (diff > 0) {
                    return 1;
                }else if (diff < 0) {
                    return -1;
                }
                return 0;
            }
        });

        map.put("contests", contests);
        ObjectMapper mapper = new ObjectMapper();
        String jsonString = null;
        try {
            jsonString = mapper.writeValueAsString(map);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }

        ApplicationHome h = new ApplicationHome(OJCollector.class);
        if (h == null || h.getSource() == null || h.getSource().getParentFile() == null) {
            return;
        }
        String dirPath1 = h.getSource().getParentFile().toString()+"/data/";
        File file = new File(dirPath1);
        if (!file.exists()) {
            if (file.mkdir()) {
            }else{
                log.error(dirPath1 + " create failed.");
            }
        }
        StreamHandler.write(dirPath1 + "otheroj.json", jsonString);
        log.info("Collect contests items " + contests.size());
    }

    public static void main(String[] args) {
        CollectContests();
    }
}
